package com.sheng.project.study.spring.demo03;

import com.sheng.project.study.spring.demo03.test1.DbConfig;
import com.sheng.project.study.spring.demo03.test1.MainConfig1;
import com.sheng.project.study.spring.demo03.test2.DbUtil;
import com.sheng.project.study.spring.demo03.test2.MailConfig;
import com.sheng.project.study.spring.demo03.test2.MainConfig2;
import com.sheng.project.study.spring.demo03.test3.BeanMyScope;
import com.sheng.project.study.spring.demo03.test3.MainConfig3;
import com.sheng.project.study.spring.demo03.test3.User;
import com.sheng.project.study.spring.demo03.test4.BeanRefreshScope;
import com.sheng.project.study.spring.demo03.test4.MailService;
import com.sheng.project.study.spring.demo03.test4.MainConfig4;
import com.sheng.project.study.spring.demo03.test4.RefreshConfigUtil;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.MapPropertySource;

import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author liusheng
 * @date 2023/07/10
 */
public class ValueTest {

    @Test
    public void test1() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(MainConfig1.class);
        context.refresh();
        DbConfig dbConfig = context.getBean(DbConfig.class);
        System.out.println(dbConfig);
    }

    @Test
    public void test2() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        /*下面这段是关键 start*/
        // 模拟从 db 中获取配置信息
        Map<String, Object> mailInfoFromDb = DbUtil.getMailInfoFromDb();
        // 将其丢在 MapPropertySource 中（MapPropertySource 类是 spring 提供的一个类，是 PropertySource 的子类）
        MapPropertySource mailPropertySource = new MapPropertySource("mail", mailInfoFromDb);
        // 将 mailPropertySource 丢在 Environment 中的 PropertySource 列表的第一个中，让优先级最高
        context.getEnvironment().getPropertySources().addFirst(mailPropertySource);
        /*上面这段是关键 end*/
        context.register(MainConfig2.class);
        context.refresh();
        MailConfig mailConfig = context.getBean(MailConfig.class);
        System.out.println(mailConfig);
        // @Value 动态刷新的问题的问题，springboot 中使用 @RefreshScope 实现了。
        // 当 @Scope 中 proxyMode 为 TARGET_CLASS 的时候，会给当前创建的 bean 通过 cglib 生成一个代理对象，通过这个代理对象来访问目标 bean 对象。
    }

    @Test
    public void test3() throws InterruptedException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 将自定义作用域注册到 spring 容器中
        context.getBeanFactory().registerScope(BeanMyScope.SCOPE_MY, new BeanMyScope());//@1
        context.register(MainConfig3.class);
        context.refresh();
        System.out.println("从容器中获取 User 对象");
        User user = context.getBean(User.class);
        // 输出这个 bean 对应的 class，一会认真看一下，这个类型是不是 User 类型的
        System.out.println("user 对象的 class 为：" + user.getClass());
        System.out.println("多次调用 user 的 getUsername 感受一下效果\n");
        for (int i = 1; i <= 3; i++) {
            System.out.println(String.format("********\n第 %d 次开始调用 getUsername", i));
            System.out.println(user.getUsername());
            System.out.println(String.format("第 %d 次调用 getUsername 结束\n********\n", i));
        }
        // 通过上面的案例可以看出，当自定义的 Scope 中 proxyMode=ScopedProxyMode.TARGET_CLASS 的时候，
        // 会给这个 bean 创建一个代理对象，调用代理对象的任何方法，
        // 都会调用这个自定义的作用域实现类（上面的 BeanMyScope）中 get 方法来重新来获取这个 bean 对象。
    }

    @Test
    public void test4() throws InterruptedException {
        // 动态 @Value 实现的关键是 @Scope 中 proxyMode 参数，值为 ScopedProxyMode.DEFAULT，会生成一个代理，通过这个代理来实现 @Value 动态刷新的效果
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.getBeanFactory().registerScope(BeanRefreshScope.SCOPE_REFRESH, BeanRefreshScope.getInstance());
        context.register(MainConfig4.class);
        // 刷新 mail 的配置到 Environment
        RefreshConfigUtil.refreshMailPropertySource(context);
        context.refresh();
        MailService mailService = context.getBean(MailService.class);
        System.out.println("配置未更新的情况下,输出 3 次");
        for (int i = 0; i < 3; i++) {
            // 循环 3 次，输出 mailService 的信息
            System.out.println(mailService);
            TimeUnit.MILLISECONDS.sleep(200);
        }
        System.out.println("模拟 3 次更新配置效果");
        for (int i = 0; i < 3; i++) {
            RefreshConfigUtil.updateDbConfig(context);
            System.out.println(mailService);
            TimeUnit.MILLISECONDS.sleep(200);
        }
    }
}
